/*
 * Collidable.cpp
 *
 *  Created on: 2009/01/22
 *      Author: Yongshik Cho
 */

#include "Collidable.h"
#include "../core/Line2D.h"
#include "../core/Triangle2D.h"
#include "../core/Matrix4.h"
#include "../SceneManager.h"
#include "../Camera.h"


Collidable::Collidable()
{
	mType = COLLIDABLE_LINE;
	mPoint = 0;
	mLine = 0;
	mTriangle = 0;
	mClosed = false;
	mPointIndex = 0;
	mLineIndex = 0;
	mTriangleIndex = 0;

	mPosition = Vector2D(0.f, 0.f);
	mRotation = 0.f;
	mScale = Vector2D(1.f, 1.f);
}

Collidable::Collidable(const uint8 type)
{
	// TODO Auto-generated constructor stub
	mType = type;
	mPoint = 0;
	mLine = 0;
	mTriangle = 0;
	mClosed = false;
	mPointIndex = 0;
	mLineIndex = 0;
	mTriangleIndex = 0;

	mPosition = Vector2D(0.f, 0.f);
	mRotation = 0.f;
	mScale = Vector2D(1.f, 1.f);
}

Collidable::Collidable(const String& name)
{
	mName = name;
	mType = COLLIDABLE_POLYGON;
	mPoint = 0;
	mLine = 0;
	mTriangle = 0;
	mClosed = false;
	mPointIndex = 0;
	mLineIndex = 0;
	mTriangleIndex = 0;

	mPosition = Vector2D(0.f, 0.f);
	mRotation = 0.f;
	mScale = Vector2D(1.f, 1.f);
}

Collidable::Collidable(Collidable& rhs)
{
	mName = rhs.mName;
	mType = rhs.mType;
	mPoint = 0;
	mLine = 0;
	mTriangle = 0;
	mClosed = rhs.mClosed;
	mPointIndex = 0;
	mLineIndex = 0;
	mTriangleIndex = 0;

	mPosition = rhs.mPosition;
	mRotation = rhs.mRotation;
	mScale = rhs.mScale;

	mPoints.clear();
	for(vector<Vector2D>::iterator it=rhs.mPoints.begin();it!=rhs.mPoints.end();++it)
	{
		mPoints.push_back(*it);
	}
}

Collidable::~Collidable()
{
	// TODO Auto-generated destructor stub
	SAFE_DELETE(mPoint);

	Line2D* line = (Line2D*)mLine;
	SAFE_DELETE(line);

	Triangle2D* tris = (Triangle2D*)mTriangle;
	SAFE_DELETE(tris);

	mPoints.clear();
}

void Collidable::addPoint(Vector2D p)
{
	if(hasPoint(p))
		return;

	mPoints.push_back(p);
}

void Collidable::subPoint(Vector2D p)
{
	for(vector<Vector2D>::iterator it=mPoints.begin();it!=mPoints.end();++it)
	{
		if(p == *it)
		{
			mPoints.erase(it);
			break;
		}
	}
}

const bool Collidable::hasPoint(Vector2D p)
{
	for(vector<Vector2D>::iterator it=mPoints.begin();it!=mPoints.end();++it)
	{
		if(p == *it)
			return true;
	}

	return false;
}

const bool Collidable::in(const Vector2D& p)
{
	if(!getLineCount())
		return false;

	setLineIndex(0);
	Line2D* line = (Line2D*)getNextLine();
	if(line && line->in(p))
		return true;

	if(!getTriangleCount())
		return false;

	setTriangleIndex(0);
	Triangle2D* tris = (Triangle2D*)getNextTriangle();
	while(tris)
	{
		if(!tris->in(p))
			return false;

		tris = (Triangle2D*)getNextTriangle();
	}

	return true;
}

const bool Collidable::in(const Collidable& rhs)
{
	if(!getTriangleCount())
		return false;

	for(uint32 i=0;i<mPoints.size();++i)
	{
		if(!in(mPoints[i]))
			return false;
	}

	return true;
}
/*
const bool Collidable::intersects(const Line2D& rhs)
{
	if(!getLineCount())
		return false;

	setLineIndex(0);
	Line2D* line = (Line2D*)getNextLine();
	while(line)
	{
		if(line->intersects(rhs))
			return true;

		line = (Line2D*)getNextLine();
	}

	return false;
}
*/
const bool Collidable::intersects(Collidable& rhs)
{
	if(!rhs.getLineCount())
		return false;

	rhs.setLineIndex(0);
	Line2D* line = (Line2D*)rhs.getNextLine();
	while(line)
	{
		if(intersects(*line))
			return true;

		line = (Line2D*)rhs.getNextLine();
	}

	return false;
}

Vector2D* Collidable::getNextPoint()
{
	if(mPointIndex >= mPoints.size())
		return 0;

	SAFE_DELETE(mPoint);
	mPoint = new Vector2D(mPoints[mPointIndex++]);

	return mPoint;
}

const uint32 Collidable::getLineCount()
{
	if(mPoints.size() <= 1)
		return 0;

	uint32 lineCount = (uint32)mPoints.size() - 1;
	if(mClosed)
		return lineCount + 1;

	return lineCount;
}

void* Collidable::getNextLine()
{
	uint32 lineCount = getLineCount();
	if(!lineCount)
		return 0;

	if(mLineIndex >= (lineCount - 1))
		return 0;

	Vector2D& p1 = mPoints[mLineIndex];
	Vector2D& p2 = mPoints[mLineIndex++];

	Line2D* line = (Line2D*)mLine;
	SAFE_DELETE(line);
	mLine = new Line2D(p1, p2);

	return mLine;
}

const uint32 Collidable::getTriangleCount()
{
	if(mPoints.size() <= 2)
		return 0;

	return (uint32)mPoints.size() - 2;
}

void* Collidable::getNextTriangle()
{
	uint32 trisCount = getTriangleCount();
	if(!trisCount)
		return 0;

	if(mTriangleIndex >= (trisCount - 1))
		return 0;

	Vector2D& p0 = *mPoints.begin();
	Vector2D& p1 = mPoints[mLineIndex];
	Vector2D& p2 = mPoints[mLineIndex++];

	Triangle2D* tris = (Triangle2D*)mTriangle;
	SAFE_DELETE(tris);
	mTriangle = new Triangle2D(p0, p1, p2);
	return mTriangle;
}

void Collidable::setPointIndex(const uint32 index)
{
	uint32 pointCount = (uint32)mPoints.size();
	if(!pointCount || index >= (pointCount -1))
		return ;

	mPointIndex = index;
}

void Collidable::setLineIndex(const uint32 index)
{
	uint32 lineCount = getLineCount();
	if(!lineCount || index >= (lineCount - 1))
		return;

	mLineIndex = index;
}

void Collidable::setTriangleIndex(const uint32 index)
{
	uint32 trisCount = getTriangleCount();
	if(!trisCount || index >= (trisCount - 1))
		return;

	mTriangleIndex = index;
}

void Collidable::setCenterType(const uint8 centerType)
{
	mCenterType = centerType;
}

void Collidable::setPosition(const Vector2D& pos)
{
	mPosition = pos;
}

void Collidable::setRotation(const float rotation)
{
	mRotation = rotation;
}

void Collidable::setScale(const Vector2D& scale)
{
	mScale = scale;
}

void Collidable::updatePoints()
{
	for(vector<Vector2D>::iterator it=mPoints.begin();it!=mPoints.end();++it)
	{
		Vector2D& p = *it;
		p = getLocalPosition(p);
	}
}

const Matrix4 Collidable::getLocalMatrix()
{
	Matrix4 mat = Matrix4::getTrans(Vector3D(mPosition.x, mPosition.y, 0.f))
		* Matrix4::getQuatRotate(Quaternion(Radian(Degree(mRotation).valueRadians()), Vector3D(0.f, 0.f, -1.f)))
		* Matrix4::getScale(Vector3D(mScale.x, mScale.y, 1.f));

	return mat;
}

const Vector2D Collidable::getLocalPosition(const Vector2D& p)
{
	//retrieves scene manager
	SceneManager& sm = SceneManager::GetInstance();

	Camera* camera = sm.getActiveCamera();
	Vector2D camScale = camera->getScale();

	Matrix4 scaleMatrix = Matrix4::getScale(Vector3D(camScale.x, camScale.y, 1.f));

	Vector4D _p = getLocalMatrix() * scaleMatrix * Vector4D(p.x, p.y, 0.f, 1.f);
	return Vector2D(_p.x, _p.y);
}